using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using MvcContrib.Pagination;
using MvcContrib.Sorting;
using MvcContrib.UI.Grid;
using TaskManager.Domain.BusinessObjects;
using TaskManager.Domain.Repositories;
using TaskManager.Domain.Services;
using TaskManager.Web.Controls;
using TaskManager.Web.Filters;
using TaskManager.Web.Mappers;
using TaskManager.Web.ViewModels.Task;

namespace TaskManager.Web.Controllers
{
    [UpdateActiveTab]
    public class TaskController : Controller
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(TaskController));

        private readonly ITaskRepository _taskRepository;
        private readonly Lazy<IProjectRepository> _projectRepository;
        private readonly IUserRepository _userRepository;
        private readonly IUnitOfWork _unitOfWork;
        private readonly Lazy<IUserActionLogger> _userActionLogger;
        private readonly Lazy<ITaskUpdateService> _taskUpdateService;
        private readonly User _loggedUser;
        private readonly TabContainer _tabContainer;

        public TaskController(ITaskRepository taskRepository, Lazy<IProjectRepository> projectRepository, IUserRepository userRepository, 
            IUnitOfWork unitOfWork, Lazy<IUserActionLogger> userActionLogger, Lazy<ITaskUpdateService> taskUpdateService, User loggedUser,
            TabContainer tabContainer)
        {
            Contract.Requires(taskRepository != null);
            Contract.Requires(userRepository != null);
            Contract.Requires(unitOfWork != null);
            Contract.Requires(projectRepository != null);
            Contract.Requires(loggedUser != null);

            _taskRepository = taskRepository;
            _projectRepository = projectRepository;
            _userRepository = userRepository;
            _unitOfWork = unitOfWork;
            _userActionLogger = userActionLogger;
            _taskUpdateService = taskUpdateService;
            _loggedUser = loggedUser;
            _tabContainer = tabContainer;

            if(log.IsDebugEnabled) log.Debug("Created TaskController");
        }

        //
        // GET: /Task/Index

        public ActionResult Index(int? page, GridSortOptions sort, bool showClosedTasks = false)
        {
            if (sort.Column == null)
                sort = new GridSortOptions { Column = "Created", Direction = SortDirection.Ascending };

            IEnumerable<Task> allTasks;

            if(showClosedTasks)
            {
                allTasks = _taskRepository.All
                   .OrderBy(sort.Column, sort.Direction)
                   .AsPagination(page ?? 1, 10);
            }
            else
            {
                allTasks = _taskRepository.All
                   .Where(task => task.StateImpl != (int)TaskState.Finished)
                   .OrderBy(sort.Column, sort.Direction)
                   .AsPagination(page ?? 1, 10);
            }

            ViewBag.sort = sort;
            ViewBag.showClosedTasks = showClosedTasks;
            ViewData["ShowAssigneeAndOwner"] = true;

            if (Request.IsAjaxRequest())
            {
                return PartialView("_Index", allTasks);
            }

            return View(allTasks);
        }

        //
        // GET: /Task/MyTasks

        public ActionResult MyTasks(int ? page, GridSortOptions sort)
        {
            if (sort.Column == null)
                sort = new GridSortOptions { Column = "Priority", Direction = SortDirection.Ascending };

            var myTasks = _taskRepository.All
                .Where(t => t.StateImpl != (int) TaskState.Finished)
                .Where(t => t.Assignee == _loggedUser.Name)
                .OrderBy(sort.Column, sort.Direction)
                .AsPagination(page ?? 1, 10);

            ViewBag.sort = sort;

            if (Request.IsAjaxRequest())
            {
                return PartialView("_MyTasks", myTasks);
            }

            return View(myTasks);
        }

        //
        // GET: /Task/OwnedByMe

        public ActionResult OwnedByMe(int? page, GridSortOptions sort)
        {
            if (sort.Column == null)
                sort = new GridSortOptions { Column = "Priority", Direction = SortDirection.Ascending };

            var ownedByMe = _taskRepository.All
                .Where(t => t.StateImpl != (int)TaskState.Finished)
                .Where(t => t.Owner == _loggedUser.Name)
                .OrderBy(sort.Column, sort.Direction)
                .AsPagination(page ?? 1, 10);

            ViewBag.sort = sort;

            if (Request.IsAjaxRequest())
            {
                return PartialView("_OwnedByMe", ownedByMe);
            }

            return View(ownedByMe);
        }

        //
        // GET: /Task/Completed

        public ActionResult Completed(int? page, GridSortOptions sort)
        {
            if (sort.Column == null)
                sort = new GridSortOptions { Column = "Updated", Direction = SortDirection.Ascending };

            var completedTasks = _taskRepository.All
                .Where(t => t.StateImpl == (int)TaskState.Finished)
                .Where(t => t.Assignee == _loggedUser.Name)
                .OrderBy(sort.Column, sort.Direction)
                .AsPagination(page ?? 1, 10);

            ViewBag.sort = sort;

            if (Request.IsAjaxRequest())
            {
                return PartialView("_Completed", completedTasks);
            }

            return View(completedTasks);
        }

        //
        // GET: /Task/Details/5

        public ViewResult Details(int id, int? tab)
        {
            Task task = _taskRepository.Find(id);

            if (tab == null || tab == TabContainer.DefaultTabId)
            {
                var routeValues = new RouteValueDictionary(HttpContext.Request.RequestContext.RouteData.Values);
                
                string tabName = task.Summary;
                
                tab = _tabContainer.AddTab(tabName, "Details", "Task", Url, routeValues).TabId;
                _tabContainer.ActiveTab = (int)tab;
            }

            ViewBag.Tab = tab;

            return View(task);
        }

        //
        // GET: /Task/Create

        public ActionResult Create()
        {
            var projects = (from p in _projectRepository.Value.All
                            where p.StateImpl == (int)ProjectState.InProgress
                            select new { p.Id, p.Name }).ToList();

            if (projects.Count == 0)
            {
                if(log.IsDebugEnabled) log.Debug("Task create action refused because there are no active projects");
                
                TempData["error"] = Resource.TaskController_Create_You_must_have_at_least_one_active_project_in_order_to_create_a_task;

                RedirectToAction("MyTasks");
            }

            ViewBag.Projects = new SelectList(projects, "Id", "Name");

            var users = _userRepository.All.ToList();
            ViewBag.Users = new SelectList(users, "Id", "Name");
            
            return View();
        }

        //
        // POST: /Task/Create
        [HttpPost]
        public ActionResult Create(TaskCreateModel model)
        {
            if (ModelState.IsValid)
            {
                Project project = _projectRepository.Value.Find(model.ProjectId);
                User assignee = _userRepository.Find(model.AssigneeId);

                var task = new Task(project, model.Summary, model.Description, _loggedUser, assignee, model.Priority,
                                     _userActionLogger.Value);

                _taskRepository.Insert(task);
                _unitOfWork.Save();
                
                return RedirectToAction("MyTasks");
            }

            var projects = (from p in _projectRepository.Value.All
                            where p.StateImpl == (int)ProjectState.InProgress
                            select new { p.Id, p.Name }).ToList();

            ViewBag.Projects = new SelectList(projects, "Id", "Name");
            
            var users = _userRepository.All.ToList();
            ViewBag.Users = new SelectList(users, "Id", "Name");

            return View();
        }

        //
        // GET: /Task/Edit/5

        public ViewResult Edit(int id)
        {
            var users = _userRepository.All.ToList();
            ViewBag.Users = new SelectList(users, "Name", "Name");

            Task task = _taskRepository.Find(id);

            var model = new TaskEditModel();
            TaskMapper.Map(task, model);

            return View(model);
        }

        //
        // POST: /Task/Edit/5

        [HttpPost]
        public ActionResult Edit(TaskEditModel model)
        {
            if (ModelState.IsValid)
            {
                Task task = _taskRepository.Find(model.Id);

                _taskUpdateService.Value.Update(task, model.Summary, model.Description, model.Priority, model.Assignee, model.Owner);

                _taskRepository.Update(task);
                _unitOfWork.Save();
                
                return RedirectToAction("Details", new {id=task.Id, tab=_tabContainer.ActiveTab});
            }
            
            return View();
        }

        //
        // POST: /Task/Start/5

        [HttpPost]
        public ViewResult Start(int id)
        {
            Task task = _taskRepository.Find(id);

            task.Start();
            _unitOfWork.Save();

            return View("Details", task);
        }

        //
        // POST: /Task/Complete/5

        [HttpPost]
        public ViewResult Complete(int id)
        {
            Task task = _taskRepository.Find(id);

            task.Complete();
            _unitOfWork.Save();

            return View("Details", task);
        }

        //
        // POST: /Task/Reopen/5

        [HttpPost]
        public ViewResult Reopen(int id)
        {
            Task task = _taskRepository.Find(id);

            task.Reopen();
            _unitOfWork.Save();

            return View("Details", task);
        }

        //
        // POST: /Task/Delete/5

        [HttpPost]
        public ActionResult Delete(int id)
        {
            _taskRepository.Delete(id);
            _unitOfWork.Save();

            return RedirectToAction("MyTasks");
        }

        //
        // GET: /Task/Log/5

        public PartialViewResult Log(int id, GridSortOptions sort, int? page)
        {
            if (sort.Column == null)
                sort = new GridSortOptions { Column = "Created", Direction = SortDirection.Ascending };

            var logs = _taskRepository.Find(id)
                .Logs
                .OrderBy(sort.Column, sort.Direction)
                .AsPagination(page ?? 1, 10);

            ViewBag.sort = sort;

            return PartialView("_TaskLog", logs);
        }

        //
        // GET: /Task/Comments/5

        public PartialViewResult Comments(int id, GridSortOptions sort, int? page)
        {
            if (sort.Column == null)
                sort = new GridSortOptions { Column = "Created", Direction = SortDirection.Ascending };

            var comments = _taskRepository.Find(id)
                .Comments
                .OrderBy(sort.Column, sort.Direction)
                .AsPagination(page ?? 1, 10);

            ViewBag.sort = sort;

            return PartialView("_TaskComments", comments);
        }

        //
        // POST: /Task/AddComment/5

        [HttpPost]
        public ViewResult AddComment(int id, string userComment)
        {
            var task = _taskRepository.Find(id);

            new Comment(userComment, task, _loggedUser);

            _unitOfWork.Save();

            return View("Details", task);
        }
    }
}

